package cn.tworice.system.service.user;

import cn.tworice.auth.util.LoginUserUtil;
import cn.tworice.common.framework.mail.bo.SendMailBO;
import cn.tworice.common.framework.mail.core.MailExecutor;
import cn.tworice.common.util.AgingMap;
import cn.tworice.common.util.MathUtils;
import cn.tworice.common.util.RegularTable;
import cn.tworice.common.util.StringUtils;
import cn.tworice.mybatis.query.MPJLambdaWrapperX;
import cn.tworice.mybatis.vo.PageParam;
import cn.tworice.mybatis.query.LambdaQueryWrapperX;
import cn.tworice.system.conifg.LoginProperties;
import cn.tworice.system.dao.department.po.UserDepartmentDO;
import cn.tworice.system.dao.role.po.UserRoleDO;
import cn.tworice.system.dao.user.po.UserDO;
import cn.tworice.common.util.cryption.MD5Utils;
import cn.tworice.system.dao.user.UserDao;
import cn.tworice.system.service.login.LoginService;
import cn.tworice.system.service.role.RoleService;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class UserServiceImpl extends ServiceImpl<UserDao, UserDO> implements UserService {
    @Resource
    private UserDao dao;

    @Resource
    private RoleService roleService;

    @Resource
    private SystemUserInfoService adminInfoService;

    @Resource
    private LoginService loginService;

    @Resource
    private MailExecutor mailExecutor;

    @Resource
    private LoginProperties loginProperties;

    private final AgingMap<String,String> reEmail = new AgingMap<>(10);

    /**
     * 获取所有管理员账号信息
     * @author 二饭
     */
    @Override
    public Map<String,Object> getPage(PageParam pageParam, UserDO entity) {
        return dao.selectPage(pageParam, new LambdaQueryWrapperX<UserDO>()
                .eqIfPresent(UserDO::getId, entity.getId())
                .likeIfPresent(UserDO::getLoginName, entity.getLoginName())
                .likeIfPresent(UserDO::getNickName,entity.getNickName())
                .orderByDesc(UserDO::getCreateTime)
        );
    }

    /**
     * 添加或更新一个账号
     * @return 账号标识
     */
    @Override
    public String updateOrInsert(UserDO user) {
        String id = null;
        if(StringUtils.isBlank(user.getId())){
            // 对密码进行加密
            user.setPassWord(MD5Utils.getMd5Plus(user.getPassWord()==null?loginProperties.getDefaultPassWord():user.getPassWord()));
            dao.insert(user);
        }else{
            updateById(user);
            id = user.getId();
        }
        return id;
    }

    /**
     * 删除管理员账号
     * @author 二饭
     */
    @Override
    public void delAdminByIds(String[] ids) {
        dao.deleteBatchIds(Arrays.asList(ids));
        roleService.delAdminRoleByAdmin(ids);
        adminInfoService.removeByAdminId(ids);
    }

    /**
     * 更新管理员密码
     */
    @Override
    public boolean updatePwd(String id,String pwd) {
        if(dao.update(new LambdaUpdateWrapper<UserDO>()
                .eq(UserDO::getId, id)
                .set(UserDO::getPassWord, MD5Utils.getMd5Plus(pwd))) != 1){
            throw new RuntimeException("更新账号信息异常");
        }
        return true;
    }

    @Override
    public UserDO getInfo(String id) {
        return dao.selectById(id);
    }

    /**
     * 重置密码
     * @param id 用户ID
     * @return boolean 是否重置成功
     **/
    @Override
    public boolean rePassWord(String id) {
        return this.updatePwd(id, loginProperties.getDefaultPassWord());
    }

    /**
     * 通过角色编号获取用户列表
     * @param pageParam 分页参数
     * @param roleId 角色编号
     * @return 用户列表
     */
    @Override
    public Map<String, Object> getUsersByRole(PageParam pageParam, Integer roleId) {
        return dao.selectJoinPage(pageParam, UserDO.class, new MPJLambdaWrapperX<UserDO>()
                .selectAll(UserDO.class)
                .leftJoin(UserRoleDO.class, UserRoleDO::getUserId, UserDO::getId)
                .eq(UserRoleDO::getRoleId, roleId)
        );
    }

    @Override
    public List<UserDO> getUsersByDepartmentId(Integer departmentId) {
        return dao.selectJoinList(UserDO.class,new MPJLambdaWrapperX<UserDO>()
                .leftJoin(UserDepartmentDO.class,UserDepartmentDO::getUserId,UserDO::getId)
                .eq(UserDepartmentDO::getDepartmentId,departmentId)
        );
    }

    /**
     * 获取不是这个部门的用户
     * 既不是这个部门的用户，同时也不属于任何一个部门
     * @param departmentId 部门编号
     * @return 用户列表
     */
    @Override
    public List<UserDO> getUsersByNoDepartmentId(Integer departmentId) {
        return dao.selectList(new MPJLambdaWrapperX<UserDO>()
                .select(UserDO::getId, UserDO::getLoginName, UserDO::getNickName)
                .notInSql(UserDO::getId, "SELECT user_id FROM system_user_department WHERE deleted = 0 AND department_id <> " + departmentId));
    }

    @Override
    public List<UserDO> getUsersByState(Integer status) {
        return dao.selectList(new LambdaQueryWrapperX<UserDO>()
                .select(UserDO::getId, UserDO::getLoginName, UserDO::getNickName, UserDO::getStatus)
                .eq(UserDO::getStatus, status)
        );
    }

    @Override
    public UserDO getUserByEmailAndPwd(String email, String password) {
        return dao.selectOne(UserDO::getEmail, email, UserDO::getPassWord, MD5Utils.getMd5Plus(password));
    }

    @Override
    public UserDO getUser(String loginName) {
        return dao.selectOne(UserDO::getLoginName, loginName);
    }

    /**
     * 换绑登录邮箱
     * @param newEmail 新邮箱
     * @param authCode 新邮箱验证码
     */
    @Override
    public void updateBindEmail(String newEmail,String authCode) {
        // 判断邮箱有没有被使用，判断邮箱验证码是否正确
        if (loginService.getUserByLoginName(newEmail) != null) {
            throw new RuntimeException("邮箱已经被使用");
        } else if(!this.authReEmailCode(newEmail,authCode)){
            throw new RuntimeException("邮箱验证失败");
        }else{
            // 提交更新
            this.updateUserEmail(LoginUserUtil.getLoginUserId(), newEmail);
        }
    }

    @Override
    public void sendAuthEmail(String email) {
        this.reEmail.put(email,
                mailExecutor.sendEmailCaptcha(4, email,"换绑邮箱"),
                loginProperties.getCaptchaAging());
    }

    @Override
    public synchronized String getInviteCode() {
        UserDO userDO = dao.selectById(LoginUserUtil.getLoginUserId());
        if (userDO.getInviteCode() == null) {
            return this.buildInviteCode(userDO);
        }
        return userDO.getInviteCode();
    }

    @Override
    public String getUserIdByInviteCode(String inviteCode) {
        if (inviteCode == null) {
            return null;
        }
        return Optional.ofNullable(
                dao.selectOne(
                        new LambdaQueryWrapperX<UserDO>()
                                .select(UserDO::getId)
                                .eq(UserDO::getInviteCode, inviteCode)
                )
        ).map(UserDO::getId).orElse(null);
    }

    @Override
    public Map<String,Object> getUsersByInviterId(PageParam pageParam, String inviterId) {
        return dao.selectPage(
                pageParam,
                new LambdaQueryWrapperX<UserDO>()
                        .eq(UserDO::getInviterId, inviterId)
                        .select(
                                UserDO::getId,
                                UserDO::getLoginName,
                                UserDO::getNickName,
                                UserDO::getStatus
                        )
        );
    }

    @Override
    public UserDO existUserByEmail(String email) {
        if (StringUtils.isEmpty(email)) {
            return null;
        }
        return dao.selectOne(UserDO::getEmail, email);
    }

    @Override
    public UserDO existUserByPhone(String phone) {
        if (StringUtils.isEmpty(phone)) {
            return null;
        }
        return dao.selectOne(UserDO::getPhone, phone);
    }

    /**
     * 更新用户登录名
     * @param id 用户ID
     * @param loginName 新登录名
     */
    @Override
    public void editLoginName(String id, String loginName) {
        if (StringUtils.isEmpty(id)) {
            throw new RuntimeException("登录账号不能为空");
        }
        UserDO userDO = dao.selectOne(new LambdaQueryWrapperX<UserDO>().eq(UserDO::getLoginName, loginName));
        if (userDO != null) {
            throw new RuntimeException("登录账号已被使用");
        }
        dao.update(new LambdaUpdateWrapper<UserDO>()
                .eq(UserDO::getId, id)
                .set(UserDO::getLoginName, loginName)
        );
    }

    @Override
    public UserDO getUserByNameAndPwd(String loginName, String password) {
        return dao.selectOne(UserDO::getLoginName, loginName, UserDO::getPassWord, MD5Utils.getMd5Plus(password));
    }

    @Override
    public UserDO getUserByPhoneAndPwd(String phone, String password) {
        return dao.selectOne(UserDO::getPhone, phone, UserDO::getPassWord, MD5Utils.getMd5Plus(password));
    }

    @Override
    public UserDO getUserByIDAndPwd(String id, String password) {
        return dao.selectOne(UserDO::getId, id, UserDO::getPassWord, MD5Utils.getMd5Plus(password));
    }

    @Override
    public void batchSendEmail(List<String> userId, String subject, String content) {
        List<UserDO> userList = dao.selectList(new LambdaQueryWrapperX<UserDO>().in(UserDO::getId, userId).select(UserDO::getId, UserDO::getEmail, UserDO::getLoginName));
        for (UserDO userDO : userList) {
            if(!StringUtils.isEmpty(userDO.getEmail())){
                mailExecutor.sendMail(userDO.getEmail(), subject, content);
            } else if (RegularTable.EMAIL.verify(userDO.getLoginName())) {
                mailExecutor.sendMail(userDO.getLoginName(), subject, content);
            }
        }
    }

    /**
     * 批量导入数据
     * @param list 数据列表
     * @return 数据库更新行数
     */
    @Override
    public boolean saveBatch(List<UserDO> list) {
        list.forEach(item->{
            item.setId(StringUtils.generateUuid());
            item.setPassWord(MD5Utils.getMd5Plus(item.getPassWord()));
        });
        return dao.insertBatch(list,list.size());
    }

    private void updateUserEmail(String uid, String newEmail) {
        UserDO userDO = new UserDO();
        userDO.setId(uid);
        userDO.setLoginName(newEmail);
        dao.updateById(userDO);
    }

    /**
     * 校验用户的新邮箱验证码
     * @return 是否正确
     */
    private boolean authReEmailCode(String email,String code) {
        return this.reEmail.exist(email, code);
    }

    private String buildInviteCode(UserDO userDO) {
        String inviteCode = MathUtils.getRandomNumber(6);
        String userId = this.getUserIdByInviteCode(inviteCode);
        if (userId == null) {
            userDO.setInviteCode(inviteCode);
            dao.updateById(userDO);
            return inviteCode;
        }
        return buildInviteCode(userDO);
    }

}
